package models

import (
	"context"
	"encoding/json"
	"errors"
	"inventory-system/services"
	"regexp"
	"time"

	"github.com/beego/beego/v2/core/logs"
	"github.com/olivere/elastic/v7"
)

type Product struct {
	UniqueCode   string    `json:"unique_code"`
	Name         string    `json:"name"`
	Price        float64   `json:"price"`
	StorageTime  time.Time `json:"storage_time"`
	OutboundTime time.Time `json:"outbound_time"`
	Status       string    `json:"status"`
}

// ValidateUniqueCode 验证唯一码格式（16位字母数字）
func ValidateUniqueCode(code string) bool {
	if len(code) != 16 {
		return false
	}
	matched, _ := regexp.MatchString("^[a-zA-Z0-9]{16}$", code)
	return matched
}

// IsUniqueCodeExists 检查唯一码是否已存在
func IsUniqueCodeExists(code string) (bool, error) {
	ctx := context.Background()
	client := services.GetClient()

	searchResult, err := client.Search().
		Index("products").
		Query(elastic.NewTermQuery("unique_code.keyword", code)).
		Size(1).
		Do(ctx)

	if err != nil {
		return false, err
	}

	return searchResult.Hits.TotalHits.Value > 0, nil
}

func (p *Product) Save() error {
	// 验证唯一码格式
	if !ValidateUniqueCode(p.UniqueCode) {
		return errors.New("唯一码必须是16位字母数字组合")
	}

	// 检查唯一码是否已存在（新增时检查）
	if p.Status == "in_storage" {
		exists, err := IsUniqueCodeExists(p.UniqueCode)
		if err != nil {
			return err
		}
		if exists {
			return errors.New("唯一码已存在")
		}
	}

	ctx := context.Background()
	client := services.GetClient()

	_, err := client.Index().
		Index("products").
		Id(p.UniqueCode).
		BodyJson(p).
		Do(ctx)

	if err != nil {
		logs.Error("Failed to save product:", err)
		return err
	}

	// 确保数据立即可搜索
	_, err = client.Refresh().Index("products").Do(ctx)
	return err
}

func GetProductByCode(code string) (*Product, error) {
	ctx := context.Background()
	client := services.GetClient()

	result, err := client.Get().
		Index("products").
		Id(code).
		Do(ctx)

	if err != nil {
		if elastic.IsNotFound(err) {
			return nil, errors.New("商品不存在")
		}
		logs.Error("Failed to get product:", err)
		return nil, err
	}

	// 修复：正确的JSON反序列化方式
	var product Product
	err = json.Unmarshal(result.Source, &product)
	if err != nil {
		logs.Error("Failed to unmarshal product:", err)
		return nil, err
	}

	return &product, nil
}

func GetAllProducts(page, size int) ([]Product, int64, error) {
	ctx := context.Background()
	client := services.GetClient()

	from := (page - 1) * size
	searchResult, err := client.Search().
		Index("products").
		Sort("storage_time", false).
		From(from).
		Size(size).
		Do(ctx)

	if err != nil {
		logs.Error("Failed to search products:", err)
		return nil, 0, err
	}

	var products []Product
	for _, hit := range searchResult.Hits.Hits {
		var product Product
		// 修复：正确的JSON反序列化方式
		err := json.Unmarshal(hit.Source, &product)
		if err == nil {
			products = append(products, product)
		} else {
			logs.Error("Failed to unmarshal product from search hit:", err)
		}
	}

	total := searchResult.Hits.TotalHits.Value
	return products, total, nil
}

func UpdateProduct(product *Product) error {
	return product.Save()
}

func DeleteProduct(code string) error {
	ctx := context.Background()
	client := services.GetClient()

	_, err := client.Delete().
		Index("products").
		Id(code).
		Do(ctx)

	if err != nil {
		logs.Error("Failed to delete product:", err)
		return err
	}

	_, err = client.Refresh().Index("products").Do(ctx)
	return err
}

func SearchProducts(query string, page, size int) ([]Product, int64, error) {
	ctx := context.Background()
	client := services.GetClient()

	from := (page - 1) * size
	searchResult, err := client.Search().
		Index("products").
		Query(elastic.NewMultiMatchQuery(query, "name", "unique_code")).
		From(from).
		Size(size).
		Do(ctx)

	if err != nil {
		logs.Error("Failed to search products:", err)
		return nil, 0, err
	}

	var products []Product
	for _, hit := range searchResult.Hits.Hits {
		var product Product
		// 修复：正确的JSON反序列化方式
		err := json.Unmarshal(hit.Source, &product)
		if err == nil {
			products = append(products, product)
		} else {
			logs.Error("Failed to unmarshal product from search:", err)
		}
	}

	total := searchResult.Hits.TotalHits.Value
	return products, total, nil
}
